module mips( clk, rst );
   input   clk;
   input   rst;
   
   wire    PCWr;
   wire  IFID_Wr;

   wire IDEX_Flush;
   wire IFID_Flush;

   wire [31:0] IF_PC;
   wire [31:0] ID_PC;

   wire [31:0] im_dout;
   wire [31:0] instr;

   wire [31:0]     NPC;
   wire [31:0]  NPCout;
   wire [31:0] PCPLUS4;
   wire PCSrc;
   wire [5:0] Op;
   wire [5:0] Funct;
   wire [4:0] rs;
   wire [4:0] rt;
   wire [4:0] rd;
   wire [15:0] Imm16;
   wire [25:0]    IMM;
   wire [25:0] EX_IMM;

   wire [1:0] RegDst;           
   wire       Branch;
   wire         MemR;
   wire [1:0]  Mem2R;
   wire         DMWr;
   wire         RFWr;
   wire       Alusrc;
   wire      Alusrc1;
   wire [1:0]  EXTOp;
   wire [4:0]  ALUOp;
   wire [1:0]  NPCOp;

   wire [4:0] WB_gprWeSel;
   wire [31:0]  WD;
   wire    WB_RFWr;
   wire [31:0] RD1;
   wire [31:0] RD2;

   wire [31:0] extDataOut;


   wire [1:0] EX_RegDst;            
   wire       EX_Branch;
   wire        EX_MemR;
   wire [1:0]  EX_Mem2R;
   wire         EX_DMWr;
   wire         EX_RFWr;
   wire      EX_Alusrc;
   wire      EX_Alusrc1;
   wire [4:0]  EX_ALUOp;
   wire [31:0]    EX_PC;
   wire [31:0]   EX_RD1;
   wire [31:0]   EX_RD2;
   wire [31:0]   MEM_aluDataIn2_fwB;
   wire [4:0]     EX_rs;
   wire [4:0]     EX_rt;
   wire [4:0]     EX_rd;
   wire [31:0] EX_Imm32;

   wire [4:0] gprWeSel;

   wire [31:0] aluDataOut;
   wire [31:0] aluDataIn1;
   wire [31:0] aluDataIn2;
   wire   Zero;

   wire MEM_RFWr;
   wire [1:0] MEM_Mem2R;
   wire MEM_Branch;
   wire MEM_MemR;
   wire MEM_DMWr;
   wire [4:0] MEM_gprWeSel;
   wire [31:0] MEM_aluDataOut;
   wire [31:0] MEM_PC;

   wire [31:0] dm_out;

   wire [1:0] forwardA;
   wire [1:0] forwardB;

   wire [31:0] aluDataIn1_fwA;

   wire [31:0] aluDataIn2_fwB;

   wire [1:0] WB_Mem2R;
   wire [31:0] WB_dm_out;
   wire [31:0] WB_aluDataOut;
   wire [31:0] WB_PCPLUS4;
   
   PC U_PC (
      .clk(clk), .rst(rst), .PCWr(PCWr), .NPC(NPC), .PC(IF_PC)
   ); 

   im_4k U_IM ( 
      .addr(IF_PC[11:2]) , .dout(im_dout)
   );

   IFID U_IFID (
      .clk(clk), .rst(rst), .im_dout(im_dout), .instr(instr), 
      .IF_PC(IF_PC), .ID_PC(ID_PC), 
      .IFID_Wr(IFID_Wr), .IFID_Flush(IFID_Flush), 
      .PCPLUS4(PCPLUS4)
   );

   RF U_RF (
      .A1(rs), .A2(rt), .A3(WB_gprWeSel), .WD(WD), .clk(clk), 
      .RFWr(WB_RFWr), .RD1(RD1), .RD2(RD2)
   );

   assign Op = instr[31:26];
   assign Funct = instr[5:0];
   assign rs = instr[25:21];
   assign rt = instr[20:16];
   assign rd = instr[15:11];
   assign Imm16 = instr[15:0];
   assign IMM = instr[25:0];
   
   Ctrl U_Ctrl (
      .clk(clk), 
      .RegDst(RegDst), .Branch(Branch), .MemR(MemR), .Mem2R(Mem2R), 
      .MemW(DMWr), .RegW(RFWr), .Alusrc(Alusrc), .Alusrc1(Alusrc1), 
      .ExtOp(EXTOp), .Aluctrl(ALUOp), .NPCOp(NPCOp), 
      .OpCode(Op), .funct(Funct),  
      .RD1(RD1), .RD2(RD2), .beq_zero(Zero), 
      .EX_MemR(EX_MemR), .EX_rt(EX_rt), .EX_rs(EX_rs), 
      .rt(rt), .rs(rs), .PCWr(PCWr), .IFID_Wr(IFID_Wr)
   );

   EXT U_EXT (
      .Imm16(Imm16), .EXTOp(EXTOp), .Imm32(extDataOut)
   );

   NPC U_NPC (
      .Reg(RD1), .ID_PC(ID_PC), .EX_PC(EX_PC), .NPCOp(NPCOp), 
      .EX_ALUOp(EX_ALUOp), .IMM(IMM), .EX_IMM(EX_IMM), 
      .NPC(NPCout), .PCSrc(PCSrc), 
      .aluDataIn1(aluDataIn1), .aluDataIn2(aluDataIn2), 
      .IFID_Flush(IFID_Flush), .IDEX_Flush(IDEX_Flush), 
      .PCPLUS4(PCPLUS4)
   );

   mux2 #( .WIDTH(32)
   )
   U_NPCSrc(
      .d0(PCPLUS4), .d1(NPCout), .s(PCSrc), .y(NPC)
   );

   IDEX U_IDEX (
    .clk(clk), .rst(rst), .RegW(RFWr), .EX_RegW(EX_RFWr), .Mem2R(Mem2R), .EX_Mem2R(EX_Mem2R), .Branch(Branch), .EX_Branch(EX_Branch), 
    .MemR(MemR), .EX_MemR(EX_MemR), .MemW(DMWr), .EX_MemW(EX_DMWr), .RegDst(RegDst), .EX_RegDst(EX_RegDst), .ALUOp(ALUOp), .EX_ALUOp(EX_ALUOp), 
    .Alusrc(Alusrc), .EX_Alusrc(EX_Alusrc), .Alusrc1(Alusrc1), .EX_Alusrc1(EX_Alusrc1),  
    .RD1(RD1), .EX_RD1(EX_RD1), .RD2(RD2), .EX_RD2(EX_RD2), .Imm32(extDataOut), .EX_Imm32(EX_Imm32), 
    .rt(rt), .EX_rt(EX_rt), .rd(rd), .EX_rd(EX_rd), .rs(rs), .EX_rs(EX_rs), 
    .ID_PC(ID_PC), .EX_PC(EX_PC), 
    .IMM(IMM), .EX_IMM(EX_IMM), .IDEX_Flush(IDEX_Flush)
   );

   /*
   LoadStall U_LoadHazard(
      .EX_MemR(EX_MemR), .EX_rt(EX_rt), .EX_rs(EX_rs), 
      .rt(rt), .rs(rs), .PCWr(PCWr), .IFID_Wr(IFID_Wr), 
      .MemW_out(DMWr), .RegW_out(RFWr)
   );
   */

   mux4 #( .WIDTH(5)
   )
   U_RegDst(
    .d0(EX_rt), .d1(EX_rd), .d2(5'd31), .d3(5'd0), .s(EX_RegDst), .y(gprWeSel)
   );

   mux4 #( .WIDTH(32)
   )
   U_ForwardA(
      .d0(EX_RD1), .d1(WD), .d2(MEM_aluDataOut), .d3(0), .s(forwardA), .y(aluDataIn1_fwA)
   );

   mux4 #( .WIDTH(32)
   )
   U_ForwardB(
      .d0(EX_RD2), .d1(WD), .d2(MEM_aluDataOut), .d3(0), .s(forwardB), .y(aluDataIn2_fwB)
   );
   
   assign aluDataIn1 = (EX_Alusrc1==1)?aluDataIn2_fwB:aluDataIn1_fwA;
   assign aluDataIn2 = (EX_Alusrc==1)?EX_Imm32:aluDataIn2_fwB;

   
   alu U_Alu(
   .A(aluDataIn1), .B(aluDataIn2), .ALUOp(EX_ALUOp), .C(aluDataOut)
   );

   EXMEM U_EXMEM(
    .clk(clk), .rst(rst), .EX_RegW(EX_RFWr), .MEM_RegW(MEM_RFWr), .EX_Mem2R(EX_Mem2R), .MEM_Mem2R(MEM_Mem2R), 
    .EX_Branch(EX_Branch), .MEM_Branch(MEM_Branch), .EX_MemR(EX_MemR), .MEM_MemR(MEM_MemR), 
    .EX_MemW(EX_DMWr), .MEM_MemW(MEM_DMWr), .gprWeSel(gprWeSel), .MEM_gprWeSel(MEM_gprWeSel), 
    .aluDataIn2_fwB(aluDataIn2_fwB), .MEM_aluDataIn2_fwB(MEM_aluDataIn2_fwB), .aluDataOut(aluDataOut), .MEM_aluDataOut(MEM_aluDataOut), 
    .EX_PC(EX_PC), .MEM_PC(MEM_PC)
   );

   dm_4k U_DMem(
    .addr(MEM_aluDataOut[11:2]), .din(MEM_aluDataIn2_fwB), .DMWr(MEM_DMWr), .clk(clk), .dout(dm_out) 
   );

   Forward U_Forward(
    .MEM_gprWeSel(MEM_gprWeSel), .WB_gprWeSel(WB_gprWeSel), .EX_rt(EX_rt), .EX_rs(EX_rs), 
    .MEM_RegW(MEM_RFWr), .WB_RegW(WB_RFWr), .forwardA(forwardA), .forwardB(forwardB), 
    .EX_RegDst(EX_RegDst)
   );

   MEMWB U_MEMWB(
    .clk(clk), .rst(rst), .MEM_RegW(MEM_RFWr), .WB_RegW(WB_RFWr), .MEM_Mem2R(MEM_Mem2R), .WB_Mem2R(WB_Mem2R), 
    .MEM_gprWeSel(MEM_gprWeSel), .WB_gprWeSel(WB_gprWeSel), 
    .dm_out(dm_out), .WB_dm_out(WB_dm_out), .MEM_aluDataOut(MEM_aluDataOut), .WB_aluDataOut(WB_aluDataOut), 
    .MEM_PC(MEM_PC), .WB_PCPLUS4(WB_PCPLUS4)
   );
   mux4 #( .WIDTH(32)
   )
   U_RegSource(
    .d0(WB_aluDataOut), .d1(WB_dm_out), .d2(WB_PCPLUS4), .d3(0), .s(WB_Mem2R), .y(WD)
   );

  
endmodule